/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#include "pch.h"
#include <journal/models/db/ink_stroke_table.h>
#include <journal/models/db/common_table.h>

using namespace std;
using namespace journal::model::db;

namespace bi = baja::ink;

ink_stroke_table::ink_stroke_table()
{
}

std::shared_ptr<journal::model::db::iink_stroke_table> ink_stroke_table::create(std::shared_ptr<s::iengine> dbEngine)
{
    std::shared_ptr<journal::model::db::ink_stroke_table> returnValue = make_shared<ink_stroke_table>();
    returnValue->_commonTableClient = std::dynamic_pointer_cast<icommon_table_client>(returnValue);
    returnValue->_commonTable = createCommonTable(returnValue->_commonTableClient, dbEngine, L"InkStroke");
    return returnValue;
}

std::shared_ptr<icommon_table> ink_stroke_table::common()
{
    return _commonTable;
}

void ink_stroke_table::defineColumns(std::shared_ptr<s::itable> table)
{
    BAJA_LOCK(_mutex);

    _inkStrokeIdColumn = s::createColumn<b::uint32>(L"ID", s::column_type::type_int32);
    table->addColumn(_inkStrokeIdColumn);
    
    _inkSurfaceIdColumn = s::createColumn<b::uint32>(L"InkSurfaceID", s::column_type::type_int32);
    table->addColumn(_inkSurfaceIdColumn);

    _inkT0Column = s::createColumn<b::uint64>(L"T0", s::column_type::type_int64);
    table->addColumn(_inkT0Column);

    _inkColorColumn = s::createColumn<b::uint32>(L"Color", s::column_type::type_int32);
    table->addColumn(_inkColorColumn);

    _inkWidthColumn = s::createColumn<b::float32>(L"Width", s::column_type::type_float32);
    table->addColumn(_inkWidthColumn);

    _inkPointsSizeColumn = s::createColumn<b::uint32>(L"PointsArraySize", s::column_type::type_int32);
    _inkPointsSizeColumn->setIsBlobSize(true);
    table->addColumn(_inkPointsSizeColumn);

    // _inkPointsColumn = s::createColumn<s::binary_blob>(L"PointsArray", s::column_type::type_binaryBlob);
    _inkPointsColumn = s::createBlobColumn(L"PointsArray", s::column_type::type_binaryBlob);
    _inkPointsColumn->setFixed(false);
    table->addColumn(_inkPointsColumn);
}

void ink_stroke_table::defineIndexColumns(std::shared_ptr<s::iindex> primaryKeyIndex)
{
    BAJA_LOCK(_mutex);

    // parent column has to be added before self to facilitate searches with parent only.
    _inkSurfaceIndexColumn = s::createIndexColumnDefinition(_inkSurfaceIdColumn, true);
    primaryKeyIndex->addIndexColumn(_inkSurfaceIndexColumn);

    _inkStrokeIndexColumn = s::createIndexColumnDefinition(_inkStrokeIdColumn, true);
    primaryKeyIndex->addIndexColumn(_inkStrokeIndexColumn);
}

void ink_stroke_table::defineRowCells(std::shared_ptr<std::vector<std::shared_ptr<s::icell>>> row)
{
    BAJA_LOCK(_mutex);

    // Bug 322: TRACK: can we get rid of this 'cell' concept?
    _inkStrokeIdCell = _inkStrokeIdColumn->createCell(0);
    _inkSurfaceIdCell = _inkSurfaceIdColumn->createCell(0);
    _inkT0IdCell = _inkT0Column->createCell(0);
    _inkColorCell = _inkColorColumn->createCell(0);
    _inkWidthCell = _inkWidthColumn->createCell(0);
    _inkPointsSizeCell = _inkPointsSizeColumn->createCell(0);
    _inkPointsCell = _inkPointsColumn->createCellByMoving(s::binary_blob(0, nullptr));

    row->push_back(_inkStrokeIdCell);
    row->push_back(_inkSurfaceIdCell);
    row->push_back(_inkT0IdCell);
    row->push_back(_inkColorCell);
    row->push_back(_inkWidthCell);
    row->push_back(_inkPointsSizeCell);
    row->push_back(_inkPointsCell);
}

void ink_stroke_table::insert(b::uint32 inkStrokeId, 
                              b::uint32 inkSurfaceId,
                              b::uint64 t0,
                              b::uint32 color,
                              b::float32 width,
                              s::binary_blob& pointsArray)
{
    BAJA_LOCK(_mutex);

    _inkStrokeIdCell->setValue(inkStrokeId);
    _inkSurfaceIdCell->setValue(inkSurfaceId);
    _inkT0IdCell->setValue(t0);
    _inkColorCell->setValue(color);
    _inkWidthCell->setValue(width);
    _inkPointsSizeCell->setValue(pointsArray._size);
    _inkPointsCell->setValueByMoving(pointsArray);

    _commonTable->insert(_commonTable->stagingRow());
}

void ink_stroke_table::update(b::uint32 inkStrokeId, 
                              b::uint32 inkSurfaceId,
                              b::uint64 t0,
                              b::uint32 color,
                              b::float32 width,
                              s::binary_blob& pointsArray)
{
    BAJA_LOCK(_mutex);

    _inkStrokeIdCell->setValue(inkStrokeId);
    _inkSurfaceIdCell->setValue(inkSurfaceId);
    _inkT0IdCell->setValue(t0);
    _inkColorCell->setValue(color);
    _inkWidthCell->setValue(width);
    _inkPointsSizeCell->setValue(pointsArray._size);
    _inkPointsCell->setValueByMoving(pointsArray);

    _commonTable->update(_commonTable->stagingRow(),
                         static_cast<baja::uint32>(_commonTable->primaryIndex()->getIndexColumns()->size()));
}

void ink_stroke_table::load(
    b::uint32 inkStrokeId, 
    b::uint32 inkSurfaceId,
    b::uint64& t0,
    b::uint32& color,
    b::float32& width,
    std::vector<bi::ink_point>& pointsArray)
{
    BAJA_LOCK(_mutex);

    // select all columns...
    std::shared_ptr<s::iresult> result = s::createResult();
    result->addColumn(_inkStrokeIdColumn);
    result->addColumn(_inkSurfaceIdColumn);
    result->addColumn(_inkT0Column);
    result->addColumn(_inkColorColumn);
    result->addColumn(_inkWidthColumn);
    result->addColumn(_inkPointsSizeColumn);
    result->addColumn(_inkPointsColumn);

    // where table.parent_id = parent_id and table.id = id

    // excute
    read(inkStrokeId, inkSurfaceId, result);

    // iterate through the result...
    std::shared_ptr<std::vector<std::shared_ptr<s::iresult_row>>> rows = result->getRows();

    BAJA_ASSERT(rows->size() == 1, L"One (and only one) records is expected.");

    std::vector<std::shared_ptr<s::iresult_row>>::iterator rowIt;
    for (rowIt = rows->begin(); rowIt < rows->end(); ++rowIt)
    {
        std::shared_ptr<s::iresult_row> row = *rowIt;
        std::shared_ptr<std::vector<std::shared_ptr<s::icell>>> cells = row->getCells();

        b::uint32 pointsArraySize = 0;

        std::vector<std::shared_ptr<s::icell>>::iterator cellIt;
        for (cellIt = cells->begin(); cellIt < cells->end(); ++cellIt)
        {
            std::shared_ptr<s::icell> cell = *cellIt;
            if( cell->getColumnId() == _inkStrokeIdColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<b::uint32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::uint32>>(cell);
                if (inkStrokeId == typedCell->getValue())
                {
                    continue;
                }
                else
                {
                    BAJA_ASSERT(false, L"The query must return the requested row only!");
                }
            }
            if( cell->getColumnId() == _inkSurfaceIdColumn->getId() )
            {
                // we already know this one...
                std::shared_ptr<s::icell_typed<b::uint32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::uint32>>(cell);
                BAJA_ASSERT(inkSurfaceId == typedCell->getValue(), L"The query must return a single record!");
                continue;
            }
            if( cell->getColumnId() == _inkT0Column->getId() )
            {
                std::shared_ptr<s::icell_typed<b::uint64>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::uint64>>(cell);
                t0 = typedCell->getValue();
                continue;
            }
            if( cell->getColumnId() == _inkColorColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<b::uint32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::uint32>>(cell);
                color = typedCell->getValue();
                continue;
            }
            if( cell->getColumnId() == _inkWidthColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<b::float32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::float32>>(cell);
                width = typedCell->getValue();
                continue;
            }
            if( cell->getColumnId() == _inkPointsSizeColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<b::uint32>> typedCell = std::dynamic_pointer_cast<s::icell_typed<b::uint32>>(cell);
                pointsArraySize = typedCell->getValue();
                pointsArray.clear();
                if (pointsArraySize != 0)
                {
                    pointsArray.resize(pointsArraySize / sizeof(bi::ink_point));
                }
                continue;
            }
            if( cell->getColumnId() == _inkPointsColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<s::binary_blob>> typedCell = std::dynamic_pointer_cast<s::icell_typed<s::binary_blob>>(cell);
                s::binary_blob blob = typedCell->getValue();
                memcpy(&pointsArray[0], blob._buffer, pointsArraySize);
                continue;
            }
        }
    }
}

void ink_stroke_table::read(b::uint32 inkStrokeId,
                            b::uint32 inkSurfaceId,
                            std::shared_ptr<s::iresult> result)
{
    BAJA_LOCK(_mutex);

    _inkStrokeIdCell->setValue(inkStrokeId);
    _inkSurfaceIdCell->setValue(inkSurfaceId);

    _commonTable->read(_commonTable->stagingRow(),
                        static_cast<baja::uint32>(_commonTable->primaryIndex()->getIndexColumns()->size()),
                       result);
}

void ink_stroke_table::readIds(b::uint32 inkSurfaceId,
                               std::vector<b::uint32>& ids)
{
    BAJA_LOCK(_mutex);

    _inkSurfaceIdCell->setValue(inkSurfaceId);

    _commonTable->readIds(_commonTable->stagingRow(),
                          _inkStrokeIdColumn,
                          ids);
}

std::shared_ptr<journal::model::db::iink_stroke_table> journal::model::db::createInkStrokeTable(std::shared_ptr<s::iengine> dbEngine)
{
    return ink_stroke_table::create(dbEngine);
}
